﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using System.Collections;

namespace CWB.Common.Logging
{
    public sealed class SysLog : ISysLog
    {
        #region Log output marker
        private const string EMPTY = "NO-CLASS";

        private const string PERFORMANCE = "[Performance]";
        private const string SECURITY = "[Security Violation]";
        private const string DEBUG = "[Debug      ]";
        private const string EXCPETION = "[Exception  ]";
        private const string SERVER = "[Server     ]";

        #endregion
        private readonly static SysLog newLogger;
        private ILog logger;

        #region Private Constructor
        /**
            * Create a logger with assembly type.  Result in logging based
            * on assembly's fully qualified name.
            * Example: WARN 10 Synet.Common.Logs.SysLog 
            */
        private SysLog(Type _type)
        {
            logger = LogManager.GetLogger(_type);
        }

        /**
        * Create a logger with an assigned name.  Result in logging based
        * on assembly's fully qualified name.
        * Example: WARN 10 myclass
        * Will default to EMPTY is is null
        */
        private SysLog(String _type)
        {
            if (String.IsNullOrEmpty(_type))
                _type = EMPTY;
            logger = LogManager.GetLogger(_type);
        }
        #endregion

        /// <summary>
        /// This will be specific to each logging framework.  Need to change if logging
        /// framework is changed.  To switch to Microsoft Logging Framework (1) change the
        /// way the logger instance is invoke here.
        /// (2) replace the logging interface implementation. 
        /// Please see EntLog.cs.
        /// </summary>
        #region Static Methods to get an Instance of a Logger
        public static ISysLog GetLogger(Type _type)
        {
            if (null == newLogger)
                return (ISysLog)new SysLog(_type);
            return newLogger;
        }

        public static ISysLog GetLogger(String _type)
        {
            if (null == newLogger)
                return (ISysLog)new SysLog(_type);
            return newLogger;
        }
        #endregion

        public static bool isDisableLogger()
        {
#if DEBUG
            return false;
#else
            return false;
#endif
        }

        #region Logging Methods without parameter priority

        void ISysLog.Performance(TimeSpan time, string _Message, params object[] objs)
        {
            if (isDisableLogger()) return;
            //GlobalContext.Properties["ModuleName"] = ModuleName;
            if (logger.IsInfoEnabled)
            {
                if (objs == null || objs.Length == 0)
                {
                    logger.Info(PERFORMANCE + RequestPerformance(time, _Message));
                    return;
                }
                logger.Info(PERFORMANCE + RequestPerformance(time, string.Format(_Message, objs)));
            }
        }

        void ISysLog.Exception(string _Message, params object[] objs)
        {
            if (isDisableLogger()) return;
            //GlobalContext.Properties["ModuleName"] = ModuleName;
            if (logger.IsErrorEnabled)
            {
                if (objs == null || objs.Length == 0)
                {
                    logger.Error(EXCPETION + _Message);
                    return;
                }
                logger.Error(EXCPETION + string.Format(_Message, objs));
            }
        }

        void ISysLog.Exception(Exception error)
        {
            if (isDisableLogger()) return;
            //GlobalContext.Properties["ModuleName"] = ModuleName;
            if (logger.IsErrorEnabled)
            {
                if (error != null)
                {
                    LogError(error, 0);
                }
            }
        }

        void ISysLog.Debug(string _Message, params object[] objs)
        {
            //GlobalContext.Properties["ModuleName"] = ModuleName;
            if (isDisableLogger()) return ;
            if (logger.IsDebugEnabled)
            {
                if (objs == null || objs.Length == 0)
                {
                    logger.Debug(DEBUG + _Message);
                    return;
                }
                logger.Debug(DEBUG + string.Format(_Message, objs));
            }
        }
        #endregion

        private void LogError(Exception ex, int innerLoop)
        {
            if (isDisableLogger()) return;
            //GlobalContext.Properties["ModuleName"] = ModuleName;
            if (ex != null)
            {
                StringBuilder builder = new StringBuilder();
                builder.Append(EXCPETION);
                builder.AppendLine();
                builder.AppendLine(string.Concat("Inner exception number - ", innerLoop));
                builder.AppendLine(ex.Message);
                builder.AppendLine(ex.StackTrace);
                innerLoop++;
                if (ex.InnerException != null)
                {
                    logger.Error(builder.ToString());
                    LogError(ex.InnerException, innerLoop);
                }
                else
                {
                    builder.AppendLine("Main exception & its inner exception Log end");
                    logger.Error(builder.ToString());

                }
            }
        }
        private static readonly TimeSpan fastBound = new TimeSpan(0, 0, 1);
        string RequestPerformance(TimeSpan timeCost, string logInfo)
        {
            double timeSpan = timeCost.TotalMilliseconds;
            string performance = GetSpeedName(timeSpan);

            if (timeCost < fastBound)
            {
                return string.Format("{0} ,Request Time:[{1}]", logInfo, timeCost);
            }
            else
            {
                return string.Format("{0} ,Request Time:[{1}] {2}", logInfo, timeCost, performance);
            }
        }
        private static string GetSpeedName(double time)
        {
            return (time > 5000 && time <= 10000 ? "[SLOW]" : (time > 10000 ? "[VERYSLOW]" : string.Empty));
        }
    }
}
